/*
 * Copyright 2013 Alibaba.com All right reserved. This software is the
 * confidential and proprietary information of Alibaba.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Alibaba.com.
 */
package exp.algorithm.sic.scalerf;

import java.util.ArrayList;

import org.slf4j.Logger;

import exp.util.DatasetsUtil;
import exp.util.MatrixUtil;
import weka.classifiers.AbstractClassifier;
import weka.core.Capabilities;
import weka.core.DenseInstance;
import weka.core.Instance;
import weka.core.Instances;


/**
 * 类Pyramid.java的实现描述：TODO 类实现描述
 * 
 */
public class Pyramid extends AbstractClassifier {
	public static int octaveNum = 4;
    public ArrayList<OctaveSpace> octaves; // 该塔中一共有几个8度空间
    Instances original = null;
    float kernels[] = new float[]{-1f,1.5f,(float)1.5*1.5f,(float)(1.5*1.5*1.5)};
    public Pyramid() {
	}
    Logger log = org.slf4j.LoggerFactory.getLogger(Pyramid.class);
    public int buildOctaves(Instances source, float scale, int levelsPerOctave, float octaveSigm, int minSize) {
    	this.original = source;
    	this.octaves = new ArrayList<OctaveSpace>();
        OctaveSpace downSpace = null;
        Instances prev = source;
        int k = octaveNum;
        while (prev != null && k>0/* && prev.width >= minSize && prev.height >= minSize*/) {
        	int seq = octaveNum - k;
        	OctaveSpace osp = new OctaveSpace(kernels[seq]);
        	osp.indexInPyramid = seq;
            osp.buildClassifier(prev);
            octaves.add(osp);
            prev = havleInstances(osp.getLastGaussianImg());
            if (downSpace != null) downSpace.up = osp;
            osp.down = downSpace;
            downSpace = osp;
            scale *= 2.0;
            k--;
        }
        return (octaves.size());
    }
    
    private Instances havleInstances(Instances inst){
    	int  k = (inst.numAttributes()-1)/2;
    	String name = inst.relationName()+k;
    	Instances res = DatasetsUtil.initInstancesWithParmas(name, k, name+"_fea_", inst.numInstances(), true, inst.classAttribute());
    	int num = inst.numInstances();
    	for(int i =0;i<num;i++){
    		double newVals[] = new double[k+1]; 
    		double oldVals[] = inst.instance(i).toDoubleArray();
    		//copy class value
    		newVals[k] = oldVals[oldVals.length-1];
    		//copy feature val by step 2 and exclude class value
    		for(int j=0;j<newVals.length-2;j++){
    			newVals[j] = oldVals[j*2];
    		}
    		res.add(new DenseInstance(1.0, newVals));
    	}
    	return res;
    }
    private Instance haveInstance(Instance inst,OctaveSpace os){
    	Instances osInst = os.getInstances();
    	int factor = (original.numAttributes()-1)/(osInst.numAttributes()-1);
    	double []vals = new double[osInst.numAttributes()];
    	for(int i = 0;i<vals.length;i++){
    		vals[i] = inst.value(i*factor);
    	}
    	DenseInstance di = new DenseInstance(1.0,vals);
    	di.setDataset(osInst);
    	return di;
    }

	@Override
	public void buildClassifier(Instances data) throws Exception {
		buildOctaves(data, 0, 0, 0, 0);
	}


	@Override
	public double[] distributionForInstance(Instance instance) throws Exception {
		int k = instance.numClasses();
		double dist[] = new double[k];
		for(OctaveSpace os:this.octaves){
			//log.info("before {}" ,instance.numAttributes());
			Instance hi = haveInstance(instance,os);
			//log.info("after {}" ,hi.numAttributes());
			double res[] = os.distributionForInstance(hi);
			MatrixUtil.add(dist, res);
		}
		return dist;
	}

	@Override
	public Capabilities getCapabilities() {
		return null;
	}
	@Override
	public String toString() {
		return "Pyramid Using kernels with rf";
	}
}

